package com.sec.bnjey.bluetooth;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.sec.bnjey.global.GlobalApplication;
import com.sec.bnjey.global.GlobalConst;

@SuppressLint("NewApi")
public class BtService {

	private static final String TAG = "BtService";

	private Context mCtx;

	private OBDListener mOBDListener = null;

	private BluetoothAdapter mBluetoothAdapter = null;
	// private BroadcastReceiver mReceiver;
	private BtReceiver mBtReceiver;

	private BluetoothDevice mDevice = null;
	private String mOBDDeviceName;
	private String mOBDDeviceAddress = null;
	private boolean mOBDDevicePaired = false;

	private static BluetoothSocket mSocket = null;

	private ConnectThread mConnectThread = null;
	private ConnectionManagerThread mConnectionManagerThread = null;

	private static final Object mMutex = new Object() {
	};

	// mutex for data communication methods : only one method can work at a
	// time

	private volatile boolean mServiceStarted = false;
	// private volatile boolean mShouldTurnOff = false;

	// we're doing nothing
	public static final int STATE_NONE = 0;
	// now initiating an outgoing connection
	public static final int STATE_CONNECTING = 1;
	// now connected to a remote device
	public static final int STATE_CONNECTED = 2;
	public static final int STATE_PAIRING = 3;

	// we're doing nothing
	public static final int STATE_DISCOVERY_NONE = 0;
	// now initiating an outgoing connection
	public static final int STATE_DISCOVERY_DOING = 1;
	// now connected to a remote device
	public static final int STATE_DISCOVERY_DONE = 2;

	public static final int OBD_ERROR_BLUETOOTH_NOT_ENABLED = -1;
	public static final int OBD_ERROR_DEVICE_NOT_CONNECTED = -2;
	public static final int OBD_ERROR_NONE = 0;

	private volatile int mDiscoveryState = STATE_DISCOVERY_NONE;
	public volatile static int mConnectionState = STATE_NONE;
	private boolean mConnectionThreadIsRunning = false;
	private static BtService mBtService = null;

	byte[][] G = new byte[2000][64];

	// private constructor
	private BtService() {
		mCtx = GlobalApplication.getAppContext();

		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (mBluetoothAdapter == null) {
			return;
		}

	}

	private synchronized void setDiscoveryState(int state) {
		Log.d(TAG, "setDiscoveryState " + state);
		mDiscoveryState = state;
	}

	private synchronized void setConnectionState(int state) {
		Log.d(TAG, "setConnectionState " + state);
		mConnectionState = state;
	}

	// singleton method to get the reference of bluetooth service class object
	public synchronized static BtService getBtService() {
		if (mBtService == null)
			mBtService = new BtService();
		return mBtService;
	}

	private class BtReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();

			if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
				int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);

				Log.d(TAG, "ACTION_STATE_CHANGED state " + state);

				switch (state) {
				case BluetoothAdapter.STATE_ON:
					Log.d(TAG, "Received Broadcast : STATE_ON");

					// mConnectionManagerThread.start();
					break;

				case BluetoothAdapter.STATE_TURNING_OFF:
					closeSocket();
					Log.d(TAG, "Received Broadcast : STATE_TURNING_OFF");
					break;

				case BluetoothAdapter.STATE_TURNING_ON:
					Log.d(TAG, "Received Broadcast : STATE_TURNING_ON");
					break;

				case BluetoothAdapter.STATE_OFF:
					Log.d(TAG, "Received Broadcast : STATE_OFF");
					if (mConnectThread != null) {
						mConnectThread.cancel();
					}
					closeSocket();

					// mBluetoothAdapter.enable();
					break;

				default:
					break;
				}
			} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {

				// Get the BluetoothDevice object from the Intent
				if (intent != null) {
					BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

					Log.d(TAG, "ACTION_FOUND ");

					// Add the name and address to an array adapter to show
					// in a ListView
					// device.getName(); device.getAddress();
					if (device != null)
						Log.d(TAG, "Received Broadcast : ACTION_FOUND > name=" + device.getName() + ", addr=" + device.getAddress() + ", bstate=" + device.getBondState());
					if (device != null && device.getName() != null && device.getName().equals(mOBDDeviceName)) {// &&
																												// device.getAddress().equals(mBtService.getPairedDeviceAddress())
						Log.d(TAG, "Received Broadcast : ACTION_FOUND > name=" + device.getName() + ", addr=" + device.getAddress() + ", bstate=" + device.getBondState());
						mDevice = device;
						mOBDDeviceAddress = device.getAddress();
						setDiscoveryState(STATE_DISCOVERY_DONE);
						mBluetoothAdapter.cancelDiscovery();
						connect(mOBDDeviceAddress);
					}
				}

			} else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

				Log.d(TAG, "ACTION_ACL_CONNECTED " + device.getName() + device.getAddress());

				// RbPreference pref = new RbPreference(mCtx);
				// pref.update(RbPreference.PREF_BT_LASTDEVICE_ADDR,
				// device.getAddress());

				// if (device != null && mDeviceName !=null) {
				// if (device.getName().equals(mDeviceName)) {
				//
				// Log.d(tag,
				// "Received Broadcast : ACTION_ACL_CONNECTED > "
				// + device.getName());
				//
				setConnectionState(STATE_CONNECTED);
				// }
			} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

				Log.d(TAG, "ACTION_ACL_DISCONNECTED ");

				// if (device != null &&
				// device.getName().equals(mOBDDeviceName)) {
				Log.d(TAG, "Received Broadcast : ACTION_ACL_DISCONNECTED > " + device.getName());
				if (mConnectThread != null) {
					mConnectThread.cancel();
				}
				closeSocket();
				setConnectionState(STATE_NONE);
				setDiscoveryState(STATE_DISCOVERY_NONE);
				// }
			}

		}
	};

	// check whether data can be sent or not
	public synchronized boolean isCommReady() {
		if (isEnable() == false) {
			Log.d(TAG, "Phone's bluetooth is disabled");
			return false;
		}

		if (isOBDDeviceFound() == false) {
			Log.d(TAG, "OBD Device is not found");
			return false;
		}

		if (isOBDDeviceConnected() == false) {
			Log.d(TAG, "OBD Device is not connected");
			return false;
		}

		if (mSocket == null) {
			return false;
		}
		return true;
	}

	public void getPairedVtalkDevice() {
		// check verify paired OBD device
		mOBDDevicePaired = false;
		mOBDDeviceName = "";
		mOBDDeviceAddress = "";

		String aAddr = mBluetoothAdapter.getAddress();
		String aName = mBluetoothAdapter.getName();

		if (aAddr == null || aAddr.length() == 0)
			return;

		BluetoothDevice bdev = mBluetoothAdapter.getRemoteDevice(aAddr);

		Set<BluetoothDevice> pairedDevs = mBluetoothAdapter.getBondedDevices();

		if (pairedDevs.size() > 0) {
			for (BluetoothDevice device : pairedDevs) {
				String pAddress = device.getAddress();
				String pName = device.getName();

				Log.d(TAG, "Device " + pName + " addr " + pAddress);

				if (pName.equals(GlobalConst.OBD_DEVICE_NAME)) {

					mOBDDevicePaired = true;
					mOBDDeviceName = pName;
					mOBDDeviceAddress = pAddress;
					break;
				}
			}
		}
	}

	//
	// // called in constructor
	// private void init() {
	//
	// // mDeviceName = SettingUtil.getStringPreferenceValue(
	// // "key_bluetooth_device_name", BtConfig.OBD_DEVICE_NAME);
	//
	// // Create a BroadcastReceiver for ACTION_FOUND
	// // mReceiver =
	//
	// // mCtx.registerReceiver(mBtReceiver, filter)
	// }

	// start bluetooth service
	public void start() {
		if (mServiceStarted == true) {
			Log.e(TAG, "start Error");
			return;
		}

		// mShouldTurnOff = true;
		mServiceStarted = true;

		Log.d(TAG, "=== Bluetooth Service Start");

		connectionStart();

		// init();

	}

	public synchronized void connectionStart() {

		Log.d(TAG, "connectionStart");

		// find vtalk device on paired list
		getPairedVtalkDevice();

		// load last device information when obd device was paired
		if (mOBDDevicePaired == false) {
			Log.d(TAG, "OBD Device Not Found");

		}

		if (mConnectionState == STATE_CONNECTED) {
			Log.e(TAG, "Error : Already connected");
			return;
		}

		Log.d(TAG, "last device name " + mOBDDeviceName);
		Log.d(TAG, "last device address " + mOBDDeviceAddress);

		if (mOBDDevicePaired && mOBDDeviceName != null && mOBDDeviceName.length() > 0 && mOBDDeviceAddress != null && mOBDDeviceAddress.length() > 0) {

			// connection manager
			mConnectionManagerThread = new ConnectionManagerThread();
			mConnectionManagerThread.start();
			mConnectionThreadIsRunning = true;

			setConnectionState(STATE_NONE);
			setDiscoveryState(STATE_DISCOVERY_NONE);

			receiverRegister();
		}

	}

	public void connectionThreadStop() {
		mConnectionThreadIsRunning = false;
		if (mConnectionManagerThread == null)
			return;
		try {
			mConnectionManagerThread.join();
			mConnectionManagerThread = null;
		} catch (InterruptedException e) {
		}

	}

	public boolean hasLastDevice() {
		if (mOBDDeviceName != null && mOBDDeviceName.length() > 0 && mOBDDeviceAddress != null && mOBDDeviceAddress.length() > 0)
			return true;

		return false;
	}

	private void receiverRegister() {

		if (mBtReceiver != null)
			return;

		Log.d(TAG, "receiverRegister");

		mBtReceiver = new BtReceiver();

		// Register the BroadcastReceiver
		IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
		filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
		filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
		filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
		filter.addAction(BluetoothDevice.ACTION_FOUND);
		mCtx.registerReceiver(mBtReceiver, filter); // Don't forget to
													// unregister
													// during onDestroy
	}

	private void receiverUnregister() {
		if (mBtReceiver == null)
			return;

		Log.d(TAG, "receiverUnregister");

		mCtx.unregisterReceiver(mBtReceiver);
		mBtReceiver = null;
	}

	// sj.okay - not used
	// public void start2() {
	// if (mServiceStarted == true)
	// return;
	//
	// Log.d(TAG, "******************************************************");
	// Log.d(TAG, "* Bluetooth Service Start                            *");
	// Log.d(TAG, "******************************************************");
	//
	// setConnectionState(STATE_NONE);
	// setDiscoveryState(STATE_DISCOVERY_NONE);
	//
	// // Register the BroadcastReceiver
	// IntentFilter filter = new IntentFilter(
	// BluetoothAdapter.ACTION_STATE_CHANGED);
	// filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
	// filter.addAction(BluetoothDevice.ACTION_ACL_xxED);
	// filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
	// filter.addAction(BluetoothDevice.ACTION_FOUND);
	// mCtx.registerReceiver(mReceiver, filter); // Don't forget to unregister
	// // during onDestroy
	//
	// // automatically turns on phone's bluetooth module if disabled
	//
	// // if (!mBluetoothAdapter.isEnabled()) {
	// // mShouldTurnOff = true;
	// // mBluetoothAdapter.enable();
	// // }
	//
	// mBluetoothAdapter.enable();
	//
	// mServiceStarted = true;
	// mConnectionManagerThread = new ConnectionManagerThread();
	// mConnectionManagerThread.start();
	// }

	// public void first_start() {
	// if (mServiceStarted == true)
	// return;
	//
	// mShouldTurnOff = false;
	//
	// Log.d(TAG, "******************************************************");
	// Log.d(TAG, "* Bluetooth Service Start                            *");
	// Log.d(TAG, "******************************************************");
	//
	// setConnectionState(STATE_NONE);
	// setDiscoveryState(STATE_DISCOVERY_NONE);
	//
	// // // Register the BroadcastReceiver
	// // IntentFilter filter = new IntentFilter(
	// // BluetoothAdapter.ACTION_STATE_CHANGED);
	// // filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
	// // filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
	// // filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
	// // filter.addAction(BluetoothDevice.ACTION_FOUND);
	// // mCtx.registerReceiver(mReceiver, filter); // Don't forget to
	// // unregister
	// // // during onDestroy
	// receiverRegister();
	//
	// // automatically turns on phone's bluetooth module if disabled
	//
	// // if (!mBluetoothAdapter.isEnabled()) {
	// // mShouldTurnOff = true;
	// // mBluetoothAdapter.enable();
	// // }
	//
	// mBluetoothAdapter.enable();
	//
	// mServiceStarted = true;
	// mConnectionManagerThread = new ConnectionManagerThread();
	// mConnectionManagerThread.start();
	// }

	// monitoring connection between Phone and OBD Device
	// if not connected, connect again automatically
	private class ConnectionManagerThread extends Thread {

		private int connectingRetryCount = 0; // try count for making connection

		public void run() {

			// Log.e(TAG, "ConnectionManagerThread ..ing" + mServiceStarted);
			while (mServiceStarted == true && mConnectionThreadIsRunning) {

				if (mBtService.isEnable() == false) {
					Log.e(TAG, "Error : bt is not enable");
					break;
				}

				if (BtService.mConnectionState == STATE_CONNECTED) {
					Log.e(TAG, "Error : Already connected");
					connectionThreadStop();
					break;
				}

				if (connectingRetryCount > 11) {
					Log.e(TAG, "Error : connectingRetryCount over stop");
					break;
				}

				Log.d(TAG, "=== ConnectionManagerThread : state = " + mConnectionState);

				// if (mBtService.isEnabled()) {
				switch (mConnectionState) {

				case STATE_NONE:
					// if device address already exist, connect directly
					// mBtService.mDeviceAddress = mBtService
					// .getPairedDeviceAddress();

					if (mBtService.mOBDDeviceAddress != null) {
						Log.d(TAG, "connecting.");

						setDiscoveryState(STATE_DISCOVERY_DONE);
						connect(mOBDDeviceAddress);
					} else {

						Log.d(TAG, "pairing.");
						setConnectionState(STATE_PAIRING);
						/*
						 * // OBD Device have not paired, so message to launch Bluetooth setting if ( getPopupHandler() != null ) { getPopupHandler
						 * ().sendEmptyMessage(1)OBD_CommunicationStart; setConnectionState(STATE_PAIRING); } else { // wait pairing popup possible state }
						 */
					}
					break;

				case STATE_PAIRING:
					// pairing state 에서는 디바이스가 pairing 될때 까지 기다린다....
					mBtService.mOBDDeviceAddress = mBtService.getPairedDeviceAddress();
					if (mBtService.mOBDDeviceAddress != null) {
						setDiscoveryState(STATE_DISCOVERY_DONE);
						connect(mOBDDeviceAddress);
					} else {
						// TODO:: 우선은 패어링이 될때까지 무한대로 기다린다. 나중 타임아웃을 설정하던, 통신
						// 연결을 다시 시도를 하지 않게끔 만들어야 한다.
					}
					break;

				case STATE_CONNECTING:
					connectingRetryCount++;
					// cancel connection trial after 30 seconds to prevent
					// being in infinite wait state
					if (connectingRetryCount >= 12) {
						Log.d(TAG, "connectingRetryCount = " + connectingRetryCount);

						// mBluetoothAdapter.disable();
						// closeSocket();
						// connectingRetryCount = 0;
					} else {
						connect(mOBDDeviceAddress);
					}
					break;

				case STATE_CONNECTED:
					connectionThreadStop();
					break;

				default:
					break;

				}

				try {
					sleep(1000);

				} catch (InterruptedException e) {
				}
			}
		}

	}

	// start device discovery or try to connect to OBD Device
	@SuppressWarnings("unused")
	private synchronized void startDeviceDiscovery() {

		Log.d(TAG, "startDeviceDiscovery");
		if (isEnable() == true) {
			if (mDiscoveryState == STATE_DISCOVERY_NONE) {
				setDiscoveryState(STATE_DISCOVERY_DOING);
				boolean result = mBluetoothAdapter.startDiscovery();
				Log.d(TAG, "Start Device Discovery : " + result);
				if (result == false) {
					setDiscoveryState(STATE_DISCOVERY_NONE);
				}

			}
		}
	}

	// get paired OBD device's MAC Address
	public synchronized String getPairedDeviceAddress() {
		Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

		Log.d(TAG, "getPairedDeviceAddress : " + pairedDevices.size());
		// If there are paired devices
		if (pairedDevices.size() > 0) {
			// Loop through paired devices
			for (BluetoothDevice device : pairedDevices) {

				Log.d(TAG, "getPairedDeviceAddress = " + device.getName() + ", " + device.getAddress());
				// Add the name and address to an array adapter to show in a
				// ListView
				if (device.getName().equals(mOBDDeviceName)) {
					Log.d(TAG, "Paired OBD Device : Name = " + device.getName() + ", Address = " + device.getAddress());
					return device.getAddress();
				}
			}
		}
		return null;
	}

	public synchronized String getPairedDeviceAddressByAddressRange() {
		Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
		// If there are paired devices
		if (pairedDevices.size() > 0) {
			// Loop through paired devices
			for (BluetoothDevice device : pairedDevices) {

				Log.d(TAG, "getPairedDeviceAddressByAddressRange = " + device.getName() + ", " + device.getAddress());
				// Add the name and address to an array adapter to show in a
				// ListView
				if (device.getAddress().startsWith("00:13:7B:")) {
					Log.d(TAG, "Paired OBD Device : Name = " + device.getName() + ", Address = " + device.getAddress());
					return device.getAddress();
				}
			}
		}
		return null;
	}

	// connect to OBD Device
	private synchronized void connect(String mDeviceAddress) {
		Log.d(TAG, "connect " + mDeviceAddress);

		if (isEnable() == false) { // || isOBDDeviceFound() == false) {
			Log.e(TAG, "connect Error");
			return;
		}
		if (mDeviceAddress == null || mDeviceAddress.length() == 0) {
			Log.d(TAG, "mDeviceAddress is null ");
			return;
		}
		mDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
		if (mDevice == null) {
			setDiscoveryState(STATE_DISCOVERY_NONE);
			setConnectionState(STATE_NONE);
			Log.d(TAG, "mDevice is null ");
			return;
		}

		Log.d(TAG, "mConnectionState " + mConnectionState);

		// Cancel any thread attempting to make a connection
		switch (mConnectionState) {
		// cancel current try, and try to connect againOBD_DEVICE_UUID
		case STATE_CONNECTING:

			// removed - jay
			// if (mConnectThread != null) {
			// mConnectThread.cancel();
			// mConnectThread = null;
			// }
			break;

		case STATE_NONE: // try to connect

			// removed -jay
			// if (mDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
			// mConnectThread = new ConnectThread(mDevice);
			// mConnectThread.start();
			// }

			// connect to device - jay
			Method m = null;

			try {
				// mSocket =
				// mDevice.createInsecureRfcommSocketToServiceRecord(BtConfig.OBD_DEVICE_UUID);//
				// 2.3.3 이 되면서 간단한장치와도 연결 할수있게 인증없는 연결을 허용함
				mSocket = mDevice.createInsecureRfcommSocketToServiceRecord(BtConfig.OBD_DEVICE_UUID);

			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			try {
				// Connect the device through the socket. This will block
				// until it succeeds or throws an exception

				Log.d(TAG, "ConnectThread : Connecting ....");

				setConnectionState(STATE_CONNECTING);
				mSocket.connect();

				if (mSocket != null) {
					if (mSocket.getOutputStream() != null) {
						// BtProtocol.CMD_CommunicationStart(data[0],
						// mSocket.getInputStream(), mSocket.getOutputStream());
						setConnectionState(STATE_CONNECTED);

						Log.d(TAG, "ConnectThread : Connection Succ !!!!");
					}
				}

			} catch (IOException e) {
				Log.d(TAG, "ConnectThread :Connection Fail !!!! " + e.getMessage());
				closeSocket();
			}

			break;

		case STATE_PAIRING: // pairing....
			// pairing 이 이루어 지면 connection thread를 start 한다.
			if (mDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
				mConnectThread = new ConnectThread(mDevice);
				mConnectThread.start();
			}
			break;

		case STATE_CONNECTED: // already connected
			break;

		}
	}

	// stop bluetooth service
	public void stop() {

		// if (mServiceStarted == false)
		// return;

		Log.d(TAG, "stop / mServiceStarted:" + mServiceStarted + " mode:" + mDiscoveryState);

		// Log.d(TAG,"원래 bt가 off였냐?:"+mShouldTurnOff+">> stop()");
		// Log.d(TAG,"stop() 1");

		// if (mServiceStarted == true) {
		mServiceStarted = false;
		if (mDiscoveryState == STATE_DISCOVERY_DOING) {
			Log.d(TAG, "mBluetoothAdapter.getName() : " + mBluetoothAdapter.getName());
			mBluetoothAdapter.cancelDiscovery();
			// Log.d(TAG,"stop() 2");
		}
		// Log.d(TAG,"stop() 3");
		setDiscoveryState(STATE_DISCOVERY_NONE);

		if (mConnectionState != STATE_NONE) {
			// Log.d(TAG,"stop() 4");
			if (mConnectThread != null) {
				mConnectThread.cancel();
				mConnectThread = null;

			}
		}
		// Log.d(TAG,"stop() 6");
		setConnectionState(STATE_NONE);

		// if (mReceiver != null) {
		// Log.d(TAG,"stop() 7");
		// mCtx.unregisterReceiver(mReceiver);
		// }
		// Log.d(TAG,"stop() 8");
		// mReceiver = null;

		receiverUnregister();

		// if (mShouldTurnOff == true) {
		// mBluetoothAdapter.disable();
		// Log.d(TAG, "BT OFF");
		// }

		Log.d(TAG, "=== Bluetooth Service Stop");
		// }

	}

	// public void stop2() {
	//
	// if (mServiceStarted == true) {
	//
	// mServiceStarted = false;
	//
	// if (mDiscoveryState == STATE_DISCOVERY_DOING) {
	//
	// mBluetoothAdapter.cancelDiscovery();
	// }
	// setDiscoveryState(STATE_DISCOVERY_NONE);
	//
	// if (mConnectionState != STATE_NONE) {
	// if (mConnectThread != null) {
	// mConnectThread.cancel();
	// mConnectThread = null;
	//
	// }
	// }
	//
	// setConnectionState(STATE_NONE);
	//
	// // if (mReceiver != null) {
	// // mCtx.unregisterReceiver(mReceiver);
	// // }
	// // mReceiver = null;
	// receiverUnregister();
	//
	// Log.d(TAG,
	// "******************************************************");
	// Log.d(TAG,
	// "* Bluetooth Service Stop                            *");
	// Log.d(TAG,
	// "******************************************************");
	// }
	// }

	// public boolean return_stop() {
	// if (mShouldTurnOff == true)
	// return true;
	// else
	// return false;
	// }

	public int getBluetoothStatus() {
		return mConnectionState;
	}

	public synchronized boolean isOBDDeviceFound() {
		return (mDiscoveryState == STATE_DISCOVERY_DONE);

	}

	public synchronized boolean isOBDDeviceConnected() {
		if (mConnectionState == STATE_CONNECTED)
			return true;
		else
			return false;
	}

	public synchronized boolean isOBDDevicePairing() {
		// Log.d(TAG, "isOBDDevicePairing " + mConnectionState);
		if (mConnectionState == STATE_PAIRING)
			return true;
		else
			return false;
	}

	public synchronized boolean isStarted() {
		return mServiceStarted;
	}

	public synchronized static BluetoothSocket getSocket() {
		return mSocket;
	}

	public void disconnect() {

		try {
			Log.d(TAG, "disconnect -- ");

			if (mSocket == null)
				return;

			mSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	// bluetooth's connect method is working in blocking mode, so use this
	// thread to connect to OBD Device
	private class ConnectThread extends Thread {

		private BluetoothDevice mDevice;
		private String TAG = "ConnectThread";

		public ConnectThread(BluetoothDevice device) {
			mDevice = device;
			// Get a BluetoothSocket to connect with the given BluetoothDevice
			// try {
			// MY_UUID is the app's UUID string, also used by the server code
			// mSocket =
			// device.createRfcommSocketToServiceRecord(BtConfig.OBD_DEVICE_UUID);
			/*
			 * if ( Build.VERSION.SDK_INT >= 10 ) { try { mSocket = device.createInsecureRfcommSocketToServiceRecord (BtConfig.OBD_DEVICE_UUID); Log.d(tag,
			 * "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); Log.d(tag, "ConnectThread createInsecureRfcommSocketToServiceRecord ..."); } catch (IOException e1) { Log.d(tag,
			 * "ConnectThread e1.getMessage() " + e1.getMessage()); e1.printStackTrace(); } } else {
			 */
			/*
			 * try { mSocket = device.createRfcommSocketToServiceRecord(BtConfig. OBD_DEVICE_UUID); } catch (IOException e) { // TODO Auto-generated catch block
			 * e.printStackTrace(); }
			 */
			// Method m = null;
			/*
			 * try { m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class}); Log.d(tag, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); Log.d(tag,
			 * "ConnectThread createInsecureRfcommSocket ..."); } catch (SecurityException e2) { e2.printStackTrace(); } catch (NoSuchMethodException e2) {
			 */
			/*
			 * if ( device.getBondState() == BluetoothDevice.BOND_NONE ) { try { m = device.getClass().getMethod("createBond", new Class[] {}); m.invoke(device, new Object[] {});
			 * Log.d(tag, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); Log.d(tag, "ConnectThread createBond ..."); } catch (SecurityException e) { e.printStackTrace(); } catch
			 * (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e2) { e2.printStackTrace(); } catch (IllegalAccessException e2) {
			 * e2.printStackTrace(); } catch (InvocationTargetException e2) { e2.printStackTrace(); } }
			 * 
			 * try { m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}); mSocket = (BluetoothSocket) m.invoke(device, 1); Log.d(tag,
			 * "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); Log.d(tag, "ConnectThread createRfcommSocket ..."); } catch (SecurityException e) { e.printStackTrace(); } catch
			 * (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e2) { e2.printStackTrace(); } catch (IllegalAccessException e2) {
			 * e2.printStackTrace(); } catch (InvocationTargetException e2) { e2.printStackTrace(); }
			 */

		}

		@SuppressLint("NewApi")
		public synchronized void run() {

			Method m = null;
			/*
			 * for ( int port = 1 ; port <= 3 ; port++ ) { try { Log.d(tag, "<<< createRfcommSocket port = " + port); m = mDevice.getClass().getMethod("createInsecureRfcommSocket",
			 * new Class[] {int.class}); mSocket = (BluetoothSocket) m.invoke(mDevice, Integer.valueOf(port)); Log.d(tag, ">>> createRfcommSocket port = " + port); break; } catch
			 * (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e2) { e2.printStackTrace(); }
			 * catch (IllegalAccessException e2) { e2.printStackTrace(); } catch (InvocationTargetException e2) { e2.printStackTrace(); } }
			 */
			/*
			 * createInsecureRfcommSocketToServiceRecord Level 10 ( GINGERBREAD_MR1 : Android 2.3.3, 2.3.4 ) 이상에서만 사용가능한 API
			 * 
			 * createRfcommSocketToServiceRecord Level 5 ( ECLAIR : Android 2.0 ) 이상에서 사용가능하다. -> Nexus One 에서 이 API를 사용하여 연결 시도시 계속해서 에러가 발생한다 이때는
			 * createInsecureRfcommSocketToServiceRecord 를 이용해야한다.
			 */

			try {
				// mSocket =
				// mDevice.createInsecureRfcommSocketToServiceRecord(BtConfig.OBD_DEVICE_UUID);//
				// 2.3.3 이 되면서 간단한장치와도 연결 할수있게 인증없는 연결을 허용함
				mSocket = mDevice.createInsecureRfcommSocketToServiceRecord(BtConfig.OBD_DEVICE_UUID);

			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			if (mSocket == null)
				return;

			try {
				// Connect the device through the socket. This will block
				// until it succeeds or throws an exception

				Log.d(TAG, "ConnectThread : Connecting ....");

				setConnectionState(STATE_CONNECTING);
				mSocket.connect();

				try {
					sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				// get communication type

				// byte[] a=OBD_FirmwareVersionRequest();
				// Log.d("PNI","+a")

				// byte data2[]={0x01};
				// BtProtocol.CMD_DEMO(data2,mSocket.getInputStream(),
				// mSocket.getOutputStream());

				// byte[] data =
				// BtProtocol.CMD_CommunicationType(mSocket.getInputStream(),
				// mSocket.getOutputStream());
				// Log.d(tag, "ConnectThread : Communication type");
				// start communication with searched communication type
				// if ( data != null ) {

				if (mSocket != null) {
					if (mSocket.getOutputStream() != null) {
						// BtProtocol.CMD_CommunicationStart(data[0],
						// mSocket.getInputStream(), mSocket.getOutputStream());
						// setConnectionState(STATE_CONNECTED);
						try {
							sleep(500);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						Log.d(TAG, "ConnectThread : Connection Succ !!!!");
					}
				}
				// else {
				// closeSocket();
				// Log.d(tag, "ConnectThread : Connection Fail !!!!");
				// }

			} catch (IOException e) {
				Log.d(TAG, "ConnectThread :Connection Fail !!!! " + e.getMessage());
				closeSocket();
			}

		}

		/** Will cancel an in-progress connection, and close the socket */
		public void cancel() {
			closeSocket();
		}
	}

	// do not use in method which hava synchronized( mMutex ) { } block
	public void closeSocket() {
		// synchronized( mMutex ) {

		if (mSocket == null)
			return;

		Log.d(TAG, "Close Socket !");

		setDiscoveryState(STATE_DISCOVERY_NONE);
		setConnectionState(STATE_NONE);

		if (mSocket != null) {
			// if (!mSocket.equals(null) ) {
			try {
				mSocket.getInputStream().close();
				mSocket.getOutputStream().close();
				mSocket.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			mSocket = null;
		}
		// }

	}

	public byte[] OBD_CommunicationType() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				data = BtProtocol.CMD_CommunicationType(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {

				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	public static final byte SVCDATA_RPM = 0x0C;
	public static final byte SVCDATA_SPEED = 0x0D;
	public static final byte SVCDATA_INTAKE_AIR_TEMP = 0x0F;

	// 서비스 데이터 한번요청에 한번만 보내주는 함수
	public ArrayList<OBD.SvcDataRes> OBD_ReqeustSingleServiceData(ArrayList<OBD.SvcDataReq> svcDataReqList) {

		if (svcDataReqList == null)
			return null;
		if (isCommReady() == false)
			return null;
		if (svcDataReqList.size() <= 0)
			return null;

		synchronized (mMutex) {

			// prepare request data stream
			int cnt = svcDataReqList.size();
			byte[] reqData = new byte[2 * cnt];
			for (int i = 0; i < cnt; i++) {
				reqData[i * 2] = svcDataReqList.get(i).getLen(); // service data
																	// length
				reqData[i * 2 + 1] = svcDataReqList.get(i).getId(); // service
																	// data id
				// Log.d("DBG", "SVC REQ : " + reqData[i] + ", " +
				// reqData[i+1]);
			}

			try {

				byte[] data = BtProtocol.CMD_SvcDataRequest(reqData, mSocket.getInputStream(), mSocket.getOutputStream());

				if (data == null) {
					return null;
				}
				// process result data stream

				ArrayList<OBD.SvcDataRes> svcDataResList = new ArrayList<OBD.SvcDataRes>();
				for (int i = 0, off = 0; i < svcDataReqList.size(); i++) {
					byte id = data[off++];
					byte support = data[off++];
					int len = svcDataReqList.get(i).getLen();
					byte[] valueArray = new byte[len];
					System.arraycopy(data, off, valueArray, 0, len);
					off += len;
					svcDataResList.add(new OBD.SvcDataRes(id, support, valueArray));
				}
				return svcDataResList;

			} catch (Exception e) {
				Log.d(TAG, "OBD_ReqeustSingleServiceData IOException");
				return null;
			}
		}

	}

	private ServiceDataThread serviceDataThread = null;

	public void startServiceData(Handler handler) {
		if (serviceDataThread == null) {
			serviceDataThread = new ServiceDataThread(handler);
			serviceDataThread.start();
		}

	}

	public void stopServiceData() {
		if (serviceDataThread != null) {
			serviceDataThread.cancel();
			serviceDataThread = null;
		}
	}

	class ServiceDataThread extends Thread {
		private Handler handler;
		private boolean bRunning;

		private ServiceDataThread(Handler handler) {
			this.handler = handler;
		}

		public void run() {

			bRunning = true;
			while (bRunning) {

				// check service data support
				OBD_SvcDataSupport();

				ArrayList<OBD.SvcDataReq> reqDataList = new ArrayList<OBD.SvcDataReq>();
				// Monitor status since DTCs cleared. (Includes malfunction
				// indicator lamp (MIL) status and number of DTCs.)
				reqDataList.add(new OBD.SvcDataReq((byte) 0x01, (byte) 0x04));
				reqDataList.add(new OBD.SvcDataReq((byte) 0x0C, (byte) 0x02)); // RPM
				reqDataList.add(new OBD.SvcDataReq((byte) 0x0D, (byte) 0x01)); // VEHICLE
																				// SPEED
				reqDataList.add(new OBD.SvcDataReq((byte) 0x2F, (byte) 0x01)); // FUEL
																				// LEVEL
				reqDataList.add(new OBD.SvcDataReq((byte) 0x10, (byte) 0x02)); // MAF
																				// (Mass
																				// Air
																				// Flow)
																				// Rate
				reqDataList.add(new OBD.SvcDataReq((byte) 0x11, (byte) 0x01)); // Throttle
																				// position
				reqDataList.add(new OBD.SvcDataReq((byte) 0x5A, (byte) 0x01)); // Relative
																				// accelerator
																				// pedal
																				// position

				ArrayList<OBD.SvcDataRes> resDataList = OBD_ReqeustSingleServiceData(reqDataList);

				if (resDataList != null) {
					Message msg = Message.obtain(handler, 2, resDataList);
					handler.sendMessage(msg);
				} else {
					Log.d(TAG, "Service Data Read Fail");
				}

				try {
					sleep(100);
				} catch (InterruptedException e) {
				}
			}
			bRunning = false;
		}

		public void cancel() {
			bRunning = false;
		}

		public boolean isRunning() {
			return bRunning;
		}
	}

	public byte[] OBD_SvcDataSupport() {// 에코드라이브 DATA
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {

				data = BtProtocol.CMD_SvcDataSupport(mSocket.getInputStream(), mSocket.getOutputStream());
				if (data != null) {

					// print support flag as binary format
					StringBuffer buf = new StringBuffer();
					for (int i = 0; i < data.length; i++) {
						for (int j = 7; j >= 0; j--) {
							buf.append(((data[i] >> j) & 0x01) == 0 ? '0' : '1');
						}
						buf.append(' ');
					}
					String strSupport = buf.toString();
					Log.d(TAG, "SUPPORT (" + data.length + ") : " + strSupport);
					// end

				}
			} catch (IOException e) {

			}
			return data;
		}
	}

	public int getUpgradePercentage() {
		return upgradeIdx;

	}

	private int upgradeIdx = 0;

	public boolean upgradeFirmware(String fileName) {
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;

			upgradeIdx = 0;
			int ch;

			try {

				// 1 boot
				BtProtocol.CMD_loadBootProgram(mSocket.getInputStream(), mSocket.getOutputStream());
				// Log.d("PNI", "1 Succ !!!! ");

				// 01.00 12/10/10
				// 2 boot
				byte[] a = BtProtocol.CMD_checkBootMode(mSocket.getInputStream(), mSocket.getOutputStream());
				if (a == null)
					return false;

				if (a != null) {
					if (a[0] == 0xFF)
						return false;
				}

				// Log.d("PNI", "2 Succ !!!! ");

				// 3 FW down
				FileInputStream in = new FileInputStream(fileName);

				StringBuffer buffer = null;
				int j = 0;
				int k = 0;

				while ((ch = in.read()) != -1) {
					if (j == 63) {//
						G[k][j] = (byte) ch;
						k++;
						j = 0;
					} else {
						if (buffer == null)
							buffer = new StringBuffer();
						G[k][j] = (byte) ch;
						j++;
					}
				}
				in.close();

				int curLineNumber = 0;
				// for (int i = 0; i < list.size(); i++) {
				for (int k1 = 0; k1 < 1200; k1++) {
					// String curLine = list.get(i);
					if (true) {
						String curDataStr = String.format("%04x", curLineNumber);
						byte[] p1 = BtUtil.HexAscii2Bytes(curDataStr);
						byte[] p2 = G[k1];
						byte[] plainBytes = new byte[p1.length + p2.length];
						System.arraycopy(p1, 0, plainBytes, 0, p1.length);
						System.arraycopy(p2, 0, plainBytes, p1.length, p2.length);
						try {
							Thread.sleep(1);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						try {
							byte[] data = BtProtocol.CMD_downloadAppFW(plainBytes, mSocket.getInputStream(), mSocket.getOutputStream());
							if (data != null || data.length == 2) {
								int lineNumber = BtUtil.Bytes2Int(data);
								if (lineNumber != curLineNumber) {
									Log.d(TAG, "LINE ERROR !!!!!!!!!!! " + lineNumber + ", " + curLineNumber);
									return false;
								}
							}
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						curLineNumber++;
					}
				}
				Log.d(TAG, "3 Succ !!!!: ");

				// 4 FW program
				byte[] temp = BtProtocol.CMD_programAppFW(mSocket.getInputStream(), mSocket.getOutputStream());
				if (temp != null) {
					if (temp[0] == 0xFF)
						temp = BtProtocol.CMD_programAppFW(mSocket.getInputStream(), mSocket.getOutputStream());
					if (temp[0] == 0xFF)
						return false;
				}

				Log.d(TAG, "4 Succ !!!!: ");
				// 5
				BtProtocol.CMD_changeAppMode(mSocket.getInputStream(), mSocket.getOutputStream());
				Log.d(TAG, "5 Succ !!!!");

				// 6
				// byte[] data = BtProtocol.CMD_FirmwareVersionRequest(
				// mSocket.getInputStream(), mSocket.getOutputStream());
				// // start communication with searched communication type
				// if (data != null) {
				// Log.d("PNI",
				// "6 Succ ConnectThread : Connection Succ !!!!");
				// } else {
				// Log.d("PNI",
				// "6 Fail ConnectThread : Connection Fail !!!!");
				// }

			} catch (FileNotFoundException e) {
				e.printStackTrace();
				return false;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			}

			return true;
		}

	}

	public ArrayList<OBD.SvcDataRes> requestGenericServiceData(ArrayList<OBD.SvcDataReq> reqList) throws IOException {

		synchronized (mMutex) {
			if (isCommReady() == false)
				return null;

			// prepare request data stream
			int cnt = reqList.size();
			byte[] reqData = new byte[2 * cnt];
			for (int i = 0; i < cnt; i++) {
				reqData[i * 2] = reqList.get(i).getLen(); // service data length
				reqData[i * 2 + 1] = reqList.get(i).getId(); // service data id
			}

			byte[] data = BtProtocol.CMD_SvcDataRequest(reqData, mSocket.getInputStream(), mSocket.getOutputStream());

			if (data == null) {
				return null;
			}

			// process result data stream
			ArrayList<OBD.SvcDataRes> sdList = new ArrayList<OBD.SvcDataRes>();
			for (int i = 0, off = 0; i < reqList.size(); i++) {
				byte id = data[off++];
				byte support = data[off++];
				int len = reqList.get(i).getLen();
				byte[] valueArray = new byte[len];
				System.arraycopy(data, off, valueArray, 0, len);
				off += len;
				sdList.add(new OBD.SvcDataRes(id, support, valueArray));
			}
			return sdList;
		}
	}

	private int mOBDCommandTimeOut = 30; // sec
	public final static int OBD_GET_COMMUNICATION_START = 0;
	public final static int OBD_GET_VIN_READ = 1;
	public final static int OBD_GET_ETC_READ = 2;
	public final static int OBD_GET_TROUBLECODE_REQ = 3;
	public final static int OBD_GET_TROUBLECODE_DEL = 4;
	public final static int OBD_GET_FWVERSION_REQ = 5;
	public final static int OBD_GET_FWVERSION_WRITE = 6;

	public final static int OBD_GET_MILEAGE_READ = 7;
	public final static int OBD_GET_MILEAGE_SET = 8;
	public final static int OBD_GET_COMMUNICATION_STOP = 9;
	public final static int OBD_GET_COMMUNICATION_STOP2 = 10;

	public final static int OBD_GET_SN_READ = 11;

	public final static int OBD_SET_FUEL_TYPE = 12;
	public final static int OBD_SET_ENGINE_VOL = 13;
	public final static int OBD_SET_CYLENDER = 14;
	public final static int OBD_GET_MAF = 15;
	public final static int OBD_SET_MAF = 16;
	public final static int OBD_SET_REALMILEAGE = 17;
	public final static int OBD_GET_VOLTAGE = 18;
	public final static int OBD_GET_ACCUMULATE_MILEAGE = 19;
	public final static int OBD_SET_TEAM_DRIVER = 20;
	public final static int OBD_GET_REAL_DRIVER = 21;
	public final static int OBD_SET_RTC_TIME = 22;
	public final static int OBD_SET_SN = 23;
	public final static int OBD_SET_CONFIRM = 24;

	public final static int OBD_SET_GENERICSVS_DATA = 25;
	public final static int OBD_DEMO = 26;

	Handler OBDRequestTimer = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);

			// timeout.
			// if (mOBDRequestThread.isAlive()) {
			// Log.e(TAG, "Time out:");
			// mOBDRequestThread.interrupted();
			// mOBDListener.onOBDEvent(mOBDRequestType, null);
			// }

		}

	};

	int mOBDRequestType;
	// int reqType;
	byte[] retData = null;
	byte[] reqData = null;

	public void OBD_Request(final int reqType, byte[] parReqData, OBDListener listener) {

		Log.d(TAG, "OBD_Request type:" + reqType);

		OBDRequestTimer.sendMessageDelayed(OBDRequestTimer.obtainMessage(0), mOBDCommandTimeOut * 1000);

		mOBDRequestType = reqType;
		retData = parReqData;
		mOBDListener = listener;

		// mOBDRequestThread = new OBDRequestThread();
		// mOBDRequestThread.start();

		new OBDRequestAsyncTask().execute();
	}

	private class OBDRequestAsyncTask extends AsyncTask<Void, Integer, Void> {

		private String TAG = "OBDRequestAsyncTask";

		public OBDRequestAsyncTask() {

			Log.d(TAG, "OBDRequestAsyncTask");
		}

		@Override
		protected Void doInBackground(Void... arg0) {
			switch (mOBDRequestType) {

			case OBD_GET_COMMUNICATION_START:
				retData = OBD_CommunicationStart();
				break;

			case OBD_GET_VIN_READ:
			case OBD_GET_ETC_READ:
			case OBD_GET_TROUBLECODE_REQ:
			case OBD_GET_TROUBLECODE_DEL:
			case OBD_GET_FWVERSION_REQ:
			case OBD_GET_FWVERSION_WRITE:

			case OBD_GET_MILEAGE_READ:
			case OBD_GET_MILEAGE_SET:
			case OBD_GET_COMMUNICATION_STOP:
			case OBD_GET_COMMUNICATION_STOP2:
				break;

			case OBD_GET_SN_READ:
				retData = OBD_SN_READ();
				break;

			case OBD_SET_FUEL_TYPE:
			case OBD_SET_ENGINE_VOL:
			case OBD_SET_CYLENDER:
			case OBD_GET_MAF:
			case OBD_SET_MAF:
			case OBD_SET_REALMILEAGE:
			case OBD_GET_VOLTAGE:
			case OBD_GET_ACCUMULATE_MILEAGE:
			case OBD_SET_TEAM_DRIVER:
				break;

			case OBD_GET_REAL_DRIVER:
				retData = OBD_REAL_DRIVE();
				break;

			case OBD_SET_RTC_TIME:
			case OBD_SET_SN:
			case OBD_SET_CONFIRM:

			case OBD_SET_GENERICSVS_DATA:
			case OBD_DEMO:

			default:
				break;
			}

			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);

			mOBDListener.onOBDEvent(mOBDRequestType, retData);
		}

	}

	public byte[] OBD_CommunicationStart() {
		synchronized (mMutex) {

			byte[] data = null;
			byte[] data2 = { (byte) 0xff };
			if (isCommReady() == false) {
				return data2;
			}

			try {
				data = BtProtocol.CMD_CommunicationStart(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			return data;
		}
	}

	public byte[] OBD_VinRead() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				data = BtProtocol.CMD_VinRead(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	public byte[] OBD_EtcRead() {
		synchronized (mMutex) {
			byte[] data = null;
			byte[] dataReq = { 0x1C/* OBD Type */, 0x51 /* Fuel Type */};
			if (isCommReady() == false)
				return data;
			try {
				data = BtProtocol.CMD_EtcRead(dataReq, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	// jay add - CMD_GET_RTCTIME
	public byte[] OBD_GetDTCListAfterEngineStart() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				data = BtProtocol.CMD_GetDTCListAfterEngineStart(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	public byte[] OBD_TroubleCodeRequest() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				data = BtProtocol.CMD_ErrorCodeRequest(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	// public byte[] OBD_GetDTCList() {
	// synchronized (mMutex) {
	// byte[] data = null;
	// if (isCommReady() == false)
	// return data;
	// try {
	// data = BtProtocol.CMD_GetDTCList(mSocket.getInputStream(),
	// mSocket.getOutputStream());
	// } catch (IOException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// return data;
	// }
	// }

	public boolean OBD_TroubleCodeDelete() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_ErrorCodeDelete(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// e.printStackTrace();
				return false;

			}
			return true;
		}
	}

	public byte[] OBD_FirmwareVersionRequest() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_FirmwareVersionRequest(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data;
		}
	}

	// ssw s/n좀 보내줘
	// public byte[] CMD_SN_VALID() {
	// synchronized (mMutex) {
	// byte[] data = null;
	// if (isCommReady()) {
	// try {
	// data = BtProtocol.CMD_SN_VALID(mSocket.getInputStream(),
	// mSocket.getOutputStream());
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// }
	// return data;
	// }
	// }

	public boolean OBD_FirmwareVersionWrite(byte[] data) {
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_FirmwareVersionWriteRequest(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public int OBD_readMileage() {
		synchronized (mMutex) {
			int mileage = -1;
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_readMileage(mSocket.getInputStream(), mSocket.getOutputStream());
					mileage = BtUtil.getMileage(data);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return mileage;
		}
	}

	public boolean OBD_setMileage(int mileage) {
		synchronized (mMutex) {
			if (isCommReady()) {
				try {
					byte[] bytes = BtUtil.getMileageBytes(mileage);
					BtProtocol.CMD_setMileage(bytes, mSocket.getInputStream(), mSocket.getOutputStream());
					return true;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return false;
		}
	}

	public byte[] OBD_CommunicationStop() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				BtProtocol.CMD_CommunicationStop(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	// ssw 모듈블투 탐색대기 모드
	public byte[] OBD_CommunicationStop2() {
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady() == false)
				return data;
			try {
				BtProtocol.CMD_CommunicationStop2(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return data;
		}
	}

	// //8월 6일 추가 프로토콜////

	public byte[] OBD_SN_READ() {// 요청
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_SN_READ(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data;
		}
	}

	public boolean OBD_FUEL_TYPE(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_FUEL_TYPE(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_ENGINE_VOLUME(byte[] data) {
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_ENGINE_VOLUME(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_VOLUMETRIC(byte[] data) {
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_VOLUMETRIC(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_CYLENDER(byte[] data) {
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_CYLENDER(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public byte[] OBD_MAF_READ() {// 요청
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_MAF_READ(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data;
		}
	}

	public boolean OBD_MAF_WRITE(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_MAF_WRITE(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_REAL_MILEAGE(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_REAL_MILEAGE(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public byte[] OBD_VOLTAGE() {// 요청
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_VOLTAGE(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data;
		}
	}

	public byte[] OBD_ACCUMULATE_MILEAGE() {// 요청
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_ACCUMULATE_MILEAGE(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			// try {
			// Thread.sleep(10);
			// } catch (InterruptedException e) {
			// e.printStackTrace();
			// }

			// Log.d("PNI", "data: " +data);
			return data;
		}
	}

	public byte[] OBD_TERM_DRIVE(byte[] data) {// 요청
		synchronized (mMutex) {
			byte[] data2 = null;
			if (isCommReady()) {
				try {
					data2 = BtProtocol.CMD_TERM_DRIVE(data, mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data2;
		}
	}

	public byte[] OBD_REAL_DRIVE() {// 요청
		synchronized (mMutex) {
			byte[] data = null;
			if (isCommReady()) {
				try {
					data = BtProtocol.CMD_REAL_DRIVE(mSocket.getInputStream(), mSocket.getOutputStream());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return data;
		}
	}

	public boolean OBD_RTC_TIME(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_RTC_TIME(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_SN_WRITE(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_SN_WRITE(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_CONFIRM(byte[] data) {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_CONFIRM(data, mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	public boolean OBD_MILEAGE_DELETE() {// 등록
		synchronized (mMutex) {
			if (isCommReady() == false)
				return false;
			try {
				BtProtocol.CMD_MILEAGE_DELETE(mSocket.getInputStream(), mSocket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
	}

	// 개조한 pid 서비스데이터 요청
	public byte[] OBD_requestGenericServiceData2(byte[] reqData) throws IOException {

		synchronized (mMutex) {
			if (isCommReady() == false)
				return null;

			// prepare request data stream
			// int cnt = reqList.size();

			byte[] data = BtProtocol.CMD_SvcDataRequest(reqData, mSocket.getInputStream(), mSocket.getOutputStream());

			if (data == null) {
				return null;
			}

			return data;
		}
	}

	public boolean OBD_DEMO(byte[] data) {// 등록
		// synchronized (mMutex) {
		// if (isCommReady() == false)
		// return false;
		try {
			BtProtocol.CMD_DEMO(data, mSocket.getInputStream(), mSocket.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return true;
		// }
	}

	// ///////////////////////////////

	private DiagnosisThread diagThread = null;

	public synchronized void startDiagnosis(Handler handler) {
		if (diagThread == null) {
			diagThread = new DiagnosisThread(handler, true);
			diagThread.start();
		}
	}

	public synchronized int startOneTimeDiagnosis(Handler handler) {
		if (diagThread != null) {
			stopDiagnosis();
		}

		if (isEnable() == false)
			return OBD_ERROR_BLUETOOTH_NOT_ENABLED;
		if (isOBDDeviceConnected() == false)
			return OBD_ERROR_DEVICE_NOT_CONNECTED;

		new DiagnosisThread(handler).start();

		return OBD_ERROR_NONE;
	}

	public synchronized void stopDiagnosis() {
		if (diagThread != null) {
			diagThread.cancel();
			diagThread = null;
		}
	}

	private class DiagnosisThread extends Thread {

		Handler handler = null;
		private boolean isLoop; // one time : false, loop : true

		private DiagnosisThread(Handler handler) {
			this.handler = handler;
			isLoop = false;
		}

		private DiagnosisThread(Handler handler, boolean isLoop) {
			this.isLoop = isLoop;
		}

		public void run() {

			// byte[] vin = mBtService.OBD_VinRead();

			// workaround : to show diagnosis animation
			try {
				sleep(1000);
			} catch (InterruptedException e) {
			}

			do {
				byte[] data = mBtService.OBD_TroubleCodeRequest();
				ArrayList<String> codeList = null;
				if (data != null) {
					codeList = BtUtil.getTroubleCodeList(data);
					// write result to history database if diagnosis code exists
					if (codeList.size() >= 0) {
						// Message msg = Message.obtain(handler, 1, null);
						Message msg = Message.obtain(handler, 1, codeList);

						handler.sendMessage(msg);

					} else {

					}
					/*
					 * for ( int i=0; i< codeList.size() ; i++ ) { Log.d("BT", "Error code : " + codeList.get(i)); }
					 */
				} else {
					Message msg = Message.obtain(handler, 1, null);
					handler.sendMessage(msg);
				}
				Log.d(TAG, "DiagnosisThread working : " + codeList);

				int cnt = 50; // 5 sec
				while (isLoop && cnt-- != 0) {
					try {
						sleep(100);
					} catch (InterruptedException e) {
					}
				}

			} while (isLoop);
		}

		public void cancel() {
			isLoop = false;
		}
	}// // pairing�� ���� bluetooth setting �޴��� �������� progress��
		// // �������� �ʰ� ��� ����Ѵ�.

	// if (isBluetoothSettingMode == true) {
	// continue;
	// }

	/*
	 * private Handler popupHandler; public void setPopupHandler(Handler handler) { popupHandler = handler; }
	 * 
	 * public Handler getPopupHandler() { return popupHandler; }
	 */

	public String getDeviceName() {
		if (mDevice != null)
			return mDevice.getName();
		else
			return null;
	}

	public String getDeviceAddress() {
		if (mDevice != null)
			return mDevice.getAddress();
		else
			return null;
	}

	class UpdateThread extends Thread {
		public void run() {

			int curLineNumber = 0;
			// for (int i = 0; i < list.size(); i++) {
			for (int k = 0; k < 1200; k++) {

				// String curLine = list.get(i);

				if (true) {

					String curDataStr = String.format("%04x", curLineNumber);

					byte[] p1 = BtUtil.HexAscii2Bytes(curDataStr);

					byte[] p2 = G[k];

					byte[] plainBytes = new byte[p1.length + p2.length];

					System.arraycopy(p1, 0, plainBytes, 0, p1.length);
					System.arraycopy(p2, 0, plainBytes, p1.length, p2.length);

					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					try {

						byte[] data = BtProtocol.CMD_downloadAppFW(plainBytes, mSocket.getInputStream(), mSocket.getOutputStream());

						// if (data != null || data.length == 2) {//
						//
						// int lineNumber = BtUtil.Bytes2Int(data);
						//
						// if (lineNumber != curLineNumber) {
						// Log.d("PNI", "LINE ERROR !!!!!!!!!!! " + lineNumber
						// +
						// ", " + curLineNumber);
						// throw new OBDException(OBDException.COMMAND);
						// }
						// }

					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						// System.exit(1);
					}
					curLineNumber++;
				}
			}

		}
	}

	public boolean isEnable() {

		if (mBluetoothAdapter.isEnabled())
			return true;

		return false;
	}

	// return bt old status
	public boolean enable() {

		boolean ret = true;
		if (mBluetoothAdapter.isEnabled() == false) {
			ret = false;
			Log.d(TAG, "Enable BT");
			mBluetoothAdapter.enable();
		}

		return ret;
	}

	public void disable() {
		if (mBluetoothAdapter.isEnabled()) {
			Log.d(TAG, "Disable BT");
			mBluetoothAdapter.disable();
		}

	}

}
